package com.scott.itcast.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.junit.Test;

/**
 * 在多线程环境下,利用读写锁设计缓存系统
 * 就是别人想我要个数据。如果我有这个数据，我直接给他数据。如果没有数据。那我去数据库中查
 * 查到之后，我存到我的内存中来。
 * 下次别人再找我要的时候。我就不要查数据库了。直接把数据给他。
 * 优点： 只要查数据库1次。可以节省时间
 * 
 * 原始的做法： 每次你要数据，你直接查数据。　查10000次。时间很多。
 * 代理的做法：每次你要数据，你直接找我。我去查数据库。查到以后后我就有了。以后我就可以直接给你。不需要每次都查数据库。 快。
 * 
 * 大家都在读数据。然后又怕某1个线程去写数据。所以挂一把读锁。
 * 然后某个线程发现没有数据。然后就挂一把写锁。（在挂写锁前释放读锁）（挂了写锁，其他的线程就不能读。必须等写完了才能读）
 * 那么别的读的线程暂时不要进来的。你等着。因为现在没有数据。
 * 然后把数据写完了。（比如在数据库中新增1条记录） 同时挂读锁 再把写锁解放。（此时写锁就降级成更新锁）
 * 这时候大家有读可以读了。（因为有数据了）
 * 这就是缓存的思想。 缓存代理
 * 
 */
public class CacheSystemTest {
	private Map<String,Object> cache = new HashMap<String,Object>();
	
	//读写锁
	private ReadWriteLock rwl = new ReentrantReadWriteLock();
	
	@Test
	public void cacheSytem() throws Exception{
		
		for(int a =0; a<10; a++){
			new Thread(new Runnable(){
				public void run() {
					String name = Thread.currentThread().getName();
					Object obj = findData("abc");
					System.out.println("["+name+"]读到数据：" +obj);
				}
				
			}).start();
		}
	}
	
	//防止多个线程到了object ="10086"; 这句代码的时候都去查数据库  同步之后就只会有1个去查
	public  synchronized Object findData(String key){
		String name = Thread.currentThread().getName();
		//上读锁  大家都可以进来读  但是不能写
		rwl.readLock().lock();
		System.out.println("["+name + "]上读锁...");
		Object object = null;
		try{
			object = cache.get(key);
			if(null == object){
				//如果某个线程发现对象为空。释放读锁。同时上一把写锁。这样，其他的线程就不能读了。
				rwl.readLock().unlock();
				System.out.println("["+name + "]释放读锁...");
				
				//上写锁
				rwl.writeLock().lock(); //如果多个线程执行到这1行代码。只要有1个线程上了 写锁。那么其他的线程就上不了写锁了。只能等待
				System.out.println("["+name + "]上写锁...");
				try{
					//因为没有数据  所以要去查数据   实际代码是从数据库中去查数据
					object ="10086";
					cache.put(key, object);
					//把object给赋值了就是写数据
					System.out.println("["+name+"]写好了数据：" +object+"这句话只会执行1次............");
				}finally{
					
					//然后上读锁     此时写锁就降级成更新锁
					rwl.readLock().lock();
					System.out.println("["+name+"]写完数据后上读锁。此时写锁就降级成更新锁...");
					
					//读到数据后释放写锁
					rwl.writeLock().unlock();
					System.out.println("["+name+"]释放写锁...");
				}
			}
		}finally{
			//释放读锁
			rwl.readLock().unlock();
			System.out.println("["+name+ "]释放读锁...");
		}
		return object;
	}

}
